﻿using NiklasKarl.WarpCam;
using Nokia.Graphics.Imaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage.Streams;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI;
using NiklasKarl.WarpCam.Native;
using NiklasKarl.WarpCam.Effects;
using System.Threading;

namespace NiklasKarl.WarpCam.Sources
{
    /*public class TiledViewfinderSource : IViewfinderSource
    {
        private Semaphore m_semaphore;

        private ICameraEffect[] m_effects;

        private Bitmap[] m_sourceBitmaps;
        private IImageProvider[] m_sources;

        private BufferWrapper m_bufferWrapper;
        private Size m_resolution;
        private Rotation m_rotation;
        private FlipMode m_flip;

        private IBuffer[] m_destBuffers;
        private Bitmap[] m_destBitmaps;

        public TiledViewfinderSource()
        {
            m_semaphore = new Semaphore(1, 1);

            m_effects = new ICameraEffect[9];

            m_destBuffers = new IBuffer[9];
            m_destBitmaps = new Bitmap[9];

            m_sourceBitmaps = new Bitmap[9];
            m_sources = new IImageProvider[9];
        }

        public IImageProvider CameraSource
        {
            get { return m_sources[4]; }
            set { m_sources[4] = value; }
        }

        public void SetEffect(int i, ICameraEffect effect)
        {
            if (i < 0 || i > 8 || i == 4)
            {
                throw new IndexOutOfRangeException();
            }

            m_effects[i] = effect;
        }

        public ICameraEffect GetEffect(int i)
        {
            if (i < 0 || i > 8 || i == 4)
            {
                throw new IndexOutOfRangeException();
            }

            return m_effects[i];
        }

        public IAsyncAction Lock()
        {
            return Task.Run(() => m_semaphore.WaitOne()).AsAsyncAction();
        }

        public void ReleaseLock()
        {
            m_semaphore.Release();
        }

        public void RenderBitmap(
            BufferWrapper bufferWrapper,
            Size resolution,
            Rotation rotation,
            FlipMode flip
            )
        {
            if (m_sources[4] != null)
            {
                #if DEBUG
                System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
                #endif

                if (!bufferWrapper.Equals(m_bufferWrapper) || resolution != m_resolution ||
                    rotation != m_rotation || flip != m_flip)
                {
                    // Preparing

                    int mapIndex = (int)m_rotation % 2;
                    int[,] map = new int[,]
                    {
                        { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
                        { 0, 1, 2, 3, 4, 5, 6, 7, 8 }
                    };

                    for (int rot = 0; rot < (int)m_rotation; rot++)
                    {
                        for (int i = 0; i < 9; i++)
                        {
                            map[(rot + 1) % 2, i] = map[rot % 2, 6 - (i % 3) * 3 + (i / 3)];
                        }
                    }

                    // Add flip

                    int resolutionWidth = (int)resolution.Width;
                    int resolutionHeight = (int)resolution.Height;

                    int frameWidth = (3 - ((int)resolutionWidth % 3)) % 3;
                    int frameHeight = (3 - ((int)resolutionHeight % 3)) % 3;

                    int tileWidth = (resolutionWidth - 2 * frameWidth) / 3;
                    int tileHeight = (resolutionHeight - 2 * frameHeight) / 3;
                    Size tileSize = new Size(tileWidth, tileHeight);

                    // First create the unfiltered bitmap
                    int x = map[mapIndex, 4] % 3;
                    int y = map[mapIndex, 4] / 3;

                    m_destBuffers[map[mapIndex, 4]] = bufferWrapper.AsBuffer(
                        (uint)(x * (frameWidth + tileWidth) + y * resolutionWidth * (frameHeight + tileHeight)) * 4,
                        (uint)(resolutionWidth * tileHeight) * 4
                        );
                    m_destBitmaps[4] = new Bitmap(tileSize, ColorMode.Bgra8888, (uint)resolutionWidth * 4, m_destBuffers[map[mapIndex, 4]]);

                    for (int i = 0; i < 9; i++)
                    {
                        if (i != 4)
                        {
                            x = map[mapIndex, i] % 3;
                            y = map[mapIndex, i] / 3;

                            m_destBuffers[map[mapIndex, i]] = bufferWrapper.AsBuffer(
                                (uint)(x * (frameWidth + tileWidth) + y * resolutionWidth * (frameHeight + tileHeight)) * 4,
                                (uint)(resolutionWidth * tileHeight) * 4
                                );
                            m_destBitmaps[i] = new Bitmap(tileSize, ColorMode.Bgra8888, (uint)resolutionWidth * 4, m_destBuffers[map[mapIndex, i]]);

                            m_sourceBitmaps[i] = new Bitmap(tileSize, ColorMode.Bgra8888, (uint)resolutionWidth * 4, m_destBuffers[map[mapIndex, 4]]);
                            m_sources[i] = new BitmapImageSource(m_sourceBitmaps[i]);
                        }
                    }

                    m_bufferWrapper = bufferWrapper;
                    m_resolution = resolution;
                    m_rotation = rotation;
                    m_flip = flip;
                }

                // Rendering
                Task[] tasks = new Task[9];
                tasks[4] = m_sources[4].GetBitmapAsync(m_destBitmaps[4], OutputOption.PreserveAspectRatio).AsTask();

                for (int i = 0; i < 9; i++)
                {
                    if (i != 4)
                    {
                        if (m_effects[i] != null)
                        {
                            tasks[i] = tasks[4].ContinueWith((t, arg) => m_effects[(int)arg].GetBitmapImage(m_sources[(int)arg], m_destBitmaps[(int)arg], m_rotation), i).Unwrap();
                        }
                    }
                }

                Task.WaitAll(tasks);

                #if DEBUG
                watch.Stop();
                System.Diagnostics.Debug.WriteLine("Rendering of TiledViewfinder took {0} ms.", watch.ElapsedMilliseconds);
                #endif
            }
        }

        public void RenderBitmapAlternative(BufferWrapper bufferWrapper, Size resolution)
        {
            if (m_sources[4] != null)
            {
                #if DEBUG
                System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
                #endif

                Task<int>[] prepareTasks = new Task<int>[9];
                Task[] renderTasks = new Task[9];

                int resolutionWidth;
                int resolutionHeight;

                int frameWidth;
                int frameHeight;

                int tileWidth;
                int tileHeight;
                Size tileSize;

                if (!bufferWrapper.Equals(m_bufferWrapper) || resolution != m_resolution)
                {
                    m_bufferWrapper = bufferWrapper;
                    m_resolution = resolution;

                    // Preparing
                    resolutionWidth = (int)resolution.Width;
                    resolutionHeight = (int)resolution.Height;

                    frameWidth = (3 - ((int)resolutionWidth % 3)) % 3;
                    frameHeight = (3 - ((int)resolutionHeight % 3)) % 3;

                    tileWidth = (resolutionWidth - 2 * frameWidth) / 3;
                    tileHeight = (resolutionHeight - 2 * frameHeight) / 3;
                    tileSize = new Size(tileWidth, tileHeight);
                    
                    // First create the unfiltered Bitmap
                    prepareTasks[4] = Task.Factory.StartNew(() =>
                        {
                            m_destBuffers[4] = bufferWrapper.AsBuffer(
                                (uint)(frameWidth + tileWidth + resolutionWidth * (frameHeight + tileHeight)) * 4,
                                (uint)(resolutionWidth * tileHeight) * 4
                                );
                            m_destBitmaps[4] = new Bitmap(tileSize, ColorMode.Bgra8888, (uint)resolutionWidth * 4, m_destBuffers[4]);

                            return 4;
                        });

                    // Now create the other Bitmaps and BitmapImageSources referencing the unfiltered Bitmap
                    for (int i = 0; i < 9; i++)
                    {
                        if (i != 4)
                        {
                            prepareTasks[i] = prepareTasks[4].ContinueWith((t, arg) =>
                                {
                                    int x = (int)arg % 3;
                                    int y = (int)arg / 3;

                                    m_destBuffers[(int)arg] = bufferWrapper.AsBuffer(
                                        (uint)(x * (frameWidth + tileWidth) + y * resolutionWidth * (frameHeight + tileHeight)) * 4,
                                        (uint)(resolutionWidth * tileHeight) * 4
                                        );
                                    m_destBitmaps[(int)arg] = new Bitmap(tileSize, ColorMode.Bgra8888, (uint)resolutionWidth * 4, m_destBuffers[(int)arg]);

                                    m_sourceBitmaps[(int)arg] = new Bitmap(tileSize, ColorMode.Bgra8888, (uint)resolutionWidth * 4, m_destBuffers[4]);
                                    m_sources[(int)arg] = new BitmapImageSource(m_sourceBitmaps[(int)arg]);

                                    return (int)arg;
                                }, i);
                            
                        }
                    }
                }

                // Rendering
                if (prepareTasks[4] != null)
                {
                    renderTasks[4] = prepareTasks[4].ContinueWith((t) => m_sources[4].GetBitmapAsync(m_destBitmaps[4], OutputOption.PreserveAspectRatio).AsTask(), TaskContinuationOptions.None).Unwrap();
                }
                else
                {
                    renderTasks[4] = m_sources[4].GetBitmapAsync(m_destBitmaps[4], OutputOption.PreserveAspectRatio).AsTask();
                }

                for (int i = 0; i < 9; i++)
                {
                    if (i != 4)
                    {
                        if (m_effects[i] != null)
                        {
                            if (prepareTasks[i] != null)
                            {
                                renderTasks[i] = Task.Factory.ContinueWhenAll(
                                    new Task[] { prepareTasks[i], renderTasks[4] },
                                    (t) => { int arg = (t[0] as Task<int>).Result; m_effects[arg].GetBitmapImage(m_sources[arg], m_destBitmaps[arg], m_rotation); }
                                    );
                            }
                            else
                            {
                                renderTasks[i] = Task.Delay(50);
                                // renderTasks[i] = renderTasks[4].ContinueWith((t, arg) => m_effects[(int)arg].GetBitmapImage(m_sources[(int)arg], m_destBitmaps[(int)arg]), i, TaskContinuationOptions.None).Unwrap();
                            }
                        }
                    }
                }

                Task.WaitAll(renderTasks);

                #if DEBUG
                watch.Stop();
                System.Diagnostics.Debug.WriteLine("Rendering of TiledViewfinder took {0} ms.", watch.ElapsedMilliseconds);
                #endif
            }
        }
    }*/
}
